#include "stdafx.h"
#include "filesys/file.hpp"
#include "filesys/path.hpp"
#include "filesys/directoryiterator.hpp"
#include "stdlib/bugcheck.hpp"
#include "stdlib/exception.hpp"
using namespace JHCPP::stdlib;
using namespace JHCPP::stdlib::jhException;
#include "charset/string.hpp"
using namespace JHCPP::charset;

#if defined(JH_OS_LINUX)
#include "stream/buffer.hpp"
using namespace JHCPP::stream;
#include <algorithm>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <utime.h>
#include <cstring>
#endif

namespace JHCPP
{
	namespace filesys
	{
		#if defined(JH_OS_WINDOWS)
		/****************
		* CFileHandle   *
		****************/
		class CFileHandle
		{
		public:
			CFileHandle(const std::string& path, DWORD access, DWORD share, DWORD disp)
			{
				m_h = ::CreateFile(path.c_str(), access, share, 0, disp, 0, 0);
				if (m_h == INVALID_HANDLE_VALUE)
				{
					CFileImpl::handleLastErrorImpl(path);
				}
			}

			~CFileHandle()
			{
				if (m_h != INVALID_HANDLE_VALUE) ::CloseHandle(m_h);
			}

			HANDLE get() const
			{
				return m_h;
			}

		private:
			HANDLE m_h;
		};

		/****************
		* CFileImpl   *
		****************/
		CFileImpl::CFileImpl()
		{
		}

		CFileImpl::CFileImpl(const std::string& path): m_path(path)
		{
			std::string::size_type n = m_path.size();
			if (n > 1 && (m_path[n - 1] == '\\' || m_path[n - 1] == '/') && !((n == 3 && m_path[1]==':')))
			{
				m_path.resize(n - 1);
			}
		}

		CFileImpl::~CFileImpl()
		{
		}

		void CFileImpl::swapImpl(CFileImpl& file)
		{
			std::swap(m_path, file.m_path);
		}

		void CFileImpl::setPathImpl(const std::string& path)
		{
			m_path = path;
			std::string::size_type n = m_path.size();
			if (n > 1 && (m_path[n - 1] == '\\' || m_path[n - 1] == '/') && !((n == 3 && m_path[1]==':')))
			{
				m_path.resize(n - 1);
			}
		}

		const std::string& CFileImpl::getPathImpl() const
		{
			return m_path;
		}

		bool CFileImpl::existsImpl() const
		{
			jh_assert (!m_path.empty());

			DWORD attr = ::GetFileAttributes(m_path.c_str());
			if (attr == 0xFFFFFFFF)
			{
				switch (GetLastError())
				{
				case ERROR_FILE_NOT_FOUND:
				case ERROR_PATH_NOT_FOUND:
				case ERROR_NOT_READY:
				case ERROR_INVALID_DRIVE:
					return false;
				default:
					handleLastErrorImpl(m_path);
				}
			}
			return true;
		}

		bool CFileImpl::canReadImpl() const
		{
			jh_assert (!m_path.empty());

			DWORD attr = ::GetFileAttributes(m_path.c_str());
			if (attr == 0xFFFFFFFF)
			{
				switch (GetLastError())
				{
				case ERROR_ACCESS_DENIED:
					return false;
				default:
					handleLastErrorImpl(m_path);
				}
			}
			return true;
		}

		bool CFileImpl::canWriteImpl() const
		{
			jh_assert (!m_path.empty());

			DWORD attr = ::GetFileAttributes(m_path.c_str());
			if (attr == 0xFFFFFFFF)
				handleLastErrorImpl(m_path);
			return (attr & FILE_ATTRIBUTE_READONLY) == 0;
		}

		bool CFileImpl::canExecuteImpl() const
		{
			CPath p(m_path);
			return icompare(p.getExtension(), "exe") == 0;
		}

		bool CFileImpl::isFileImpl() const
		{
			return !isDirectoryImpl() && !isDeviceImpl();
		}


		bool CFileImpl::isDirectoryImpl() const
		{
			jh_assert (!m_path.empty());

			DWORD attr = ::GetFileAttributes(m_path.c_str());
			if (attr == 0xFFFFFFFF)
				handleLastErrorImpl(m_path);
			return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0;
		}

		bool CFileImpl::isLinkImpl() const
		{
			return false;
		}

		bool CFileImpl::isDeviceImpl() const
		{
			return
				m_path.compare(0, 4, "\\\\.\\") == 0 ||
				icompare(m_path, "CON") == 0 ||
				icompare(m_path, "PRN") == 0 ||
				icompare(m_path, "AUX") == 0 ||
				icompare(m_path, "NUL") == 0 ||
				icompare(m_path, "LPT1") == 0 ||
				icompare(m_path, "LPT2") == 0 ||
				icompare(m_path, "LPT3") == 0 ||
				icompare(m_path, "LPT4") == 0 ||
				icompare(m_path, "LPT5") == 0 ||
				icompare(m_path, "LPT6") == 0 ||
				icompare(m_path, "LPT7") == 0 ||
				icompare(m_path, "LPT8") == 0 ||
				icompare(m_path, "LPT9") == 0 ||
				icompare(m_path, "COM1") == 0 ||
				icompare(m_path, "COM2") == 0 ||
				icompare(m_path, "COM3") == 0 ||
				icompare(m_path, "COM4") == 0 ||
				icompare(m_path, "COM5") == 0 ||
				icompare(m_path, "COM6") == 0 ||
				icompare(m_path, "COM7") == 0 ||
				icompare(m_path, "COM8") == 0 ||
				icompare(m_path, "COM9") == 0;
		}

		bool CFileImpl::isHiddenImpl() const
		{
			jh_assert (!m_path.empty());

			DWORD attr = ::GetFileAttributes(m_path.c_str());
			if (attr == 0xFFFFFFFF)
				handleLastErrorImpl(m_path);
			return (attr & FILE_ATTRIBUTE_HIDDEN) != 0;
		}

		CTimeStamp CFileImpl::createdImpl() const
		{
			jh_assert (!m_path.empty());

			WIN32_FILE_ATTRIBUTE_DATA fad;
			if (::GetFileAttributesEx(m_path.c_str(), GetFileExInfoStandard, &fad) == 0) 
				handleLastErrorImpl(m_path);
			return CTimeStamp::fromFileTimeNP(fad.ftCreationTime.dwLowDateTime, fad.ftCreationTime.dwHighDateTime);
		}

		CTimeStamp CFileImpl::getLastModifiedImpl() const
		{
			jh_assert (!m_path.empty());

			WIN32_FILE_ATTRIBUTE_DATA fad;
			if (::GetFileAttributesEx(m_path.c_str(), GetFileExInfoStandard, &fad) == 0) 
				handleLastErrorImpl(m_path);
			return CTimeStamp::fromFileTimeNP(fad.ftLastWriteTime.dwLowDateTime, fad.ftLastWriteTime.dwHighDateTime);
		}

		void CFileImpl::setLastModifiedImpl(const CTimeStamp& ts)
		{
			jh_assert (!m_path.empty());

			UInt32 low;
			UInt32 high;
			ts.toFileTimeNP(low, high);
			FILETIME ft;
			ft.dwLowDateTime  = low;
			ft.dwHighDateTime = high;
			CFileHandle fh(m_path, FILE_ALL_ACCESS, FILE_SHARE_READ | FILE_SHARE_WRITE, OPEN_EXISTING);
			if (::SetFileTime(fh.get(), 0, &ft, &ft) == 0)
				handleLastErrorImpl(m_path);
		}

		CFileImpl::FileSizeImpl CFileImpl::getSizeImpl() const
		{
			jh_assert (!m_path.empty());

			WIN32_FILE_ATTRIBUTE_DATA fad;
			if (::GetFileAttributesEx(m_path.c_str(), GetFileExInfoStandard, &fad) == 0) 
				handleLastErrorImpl(m_path);
			LARGE_INTEGER li;
			li.LowPart  = fad.nFileSizeLow;
			li.HighPart = fad.nFileSizeHigh;
			return li.QuadPart;
		}

		void CFileImpl::setSizeImpl(FileSizeImpl size)
		{
			jh_assert (!m_path.empty());

			CFileHandle fh(m_path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, OPEN_EXISTING);
			LARGE_INTEGER li;
			li.QuadPart = size;
			if (::SetFilePointer(fh.get(), li.LowPart, &li.HighPart, FILE_BEGIN) == -1)
				handleLastErrorImpl(m_path);
			if (SetEndOfFile(fh.get()) == 0) 
				handleLastErrorImpl(m_path);
		}

		void CFileImpl::setWriteableImpl(bool flag)
		{
			jh_assert (!m_path.empty());

			DWORD attr = ::GetFileAttributes(m_path.c_str());
			if (attr == -1)
				handleLastErrorImpl(m_path);
			if (flag)
				attr &= ~FILE_ATTRIBUTE_READONLY;
			else
				attr |= FILE_ATTRIBUTE_READONLY;
			if (::SetFileAttributes(m_path.c_str(), attr) == 0)
				handleLastErrorImpl(m_path);
		}

		void CFileImpl::setExecutableImpl(bool flag)
		{
			// not supported
		}

		void CFileImpl::copyToImpl(const std::string& path) const
		{
			jh_assert (!m_path.empty());

			if (::CopyFile(m_path.c_str(), path.c_str(), FALSE) != 0) 
			{
				CFileImpl copy(path);
				copy.setWriteableImpl(true);
			}
			else handleLastErrorImpl(m_path);
		}

		void CFileImpl::renameToImpl(const std::string& path)
		{
			jh_assert (!m_path.empty());

			if (::MoveFile(m_path.c_str(), path.c_str()) == 0) 
				handleLastErrorImpl(m_path);
		}

		void CFileImpl::removeImpl()
		{
			jh_assert (!m_path.empty());

			if (isDirectoryImpl())
			{
				if (::RemoveDirectory(m_path.c_str()) == 0) 
					handleLastErrorImpl(m_path);
			}
			else
			{
				if (::DeleteFile(m_path.c_str()) == 0)
					handleLastErrorImpl(m_path);
			}
		}

		bool CFileImpl::createFileImpl()
		{
			jh_assert (!m_path.empty());

			HANDLE hFile = ::CreateFile(m_path.c_str(), GENERIC_WRITE, 0, 0, CREATE_NEW, 0, 0);
			if (hFile != INVALID_HANDLE_VALUE)
			{
				::CloseHandle(hFile);
				return true;
			}
			else if (::GetLastError() == ERROR_FILE_EXISTS)
				return false;
			else
				handleLastErrorImpl(m_path);
			return false;
		}

		bool CFileImpl::createDirectoryImpl()
		{
			jh_assert (!m_path.empty());

			if (existsImpl() && isDirectoryImpl())
				return false;
			if (::CreateDirectory(m_path.c_str(), 0) == 0)
				handleLastErrorImpl(m_path);
			return true;
		}

		void CFileImpl::handleLastErrorImpl(const std::string& path)
		{
			DWORD err = GetLastError();
			switch (err)
			{
			case ERROR_FILE_NOT_FOUND:
				throw FileNotFoundException(path, err);
			case ERROR_PATH_NOT_FOUND:
			case ERROR_BAD_NETPATH:
			case ERROR_CANT_RESOLVE_FILENAME:
			case ERROR_INVALID_DRIVE:
				throw PathNotFoundException(path, err);
			case ERROR_ACCESS_DENIED:
				throw FileAccessDeniedException(path, err);
			case ERROR_ALREADY_EXISTS:
			case ERROR_FILE_EXISTS:
				throw FileExistsException(path, err);
			case ERROR_INVALID_NAME:
			case ERROR_DIRECTORY:
			case ERROR_FILENAME_EXCED_RANGE:
			case ERROR_BAD_PATHNAME:
				throw PathSyntaxException(path, err);
			case ERROR_FILE_READ_ONLY:
				throw FileReadOnlyException(path, err);
			case ERROR_CANNOT_MAKE:
				throw CreateFileException(path, err);
			case ERROR_DIR_NOT_EMPTY:
				throw FileException("directory not empty", path, err);
			case ERROR_WRITE_FAULT:
				throw WriteFileException(path, err);
			case ERROR_READ_FAULT:
				throw ReadFileException(path, err);
			case ERROR_SHARING_VIOLATION:
				throw FileException("sharing violation", path, err);
			case ERROR_LOCK_VIOLATION:
				throw FileException("lock violation", path, err);
			case ERROR_HANDLE_EOF:
				throw ReadFileException("EOF reached", path, err);
			case ERROR_HANDLE_DISK_FULL:
			case ERROR_DISK_FULL:
				throw WriteFileException("disk is full", path, err);
			case ERROR_NEGATIVE_SEEK:
				throw FileException("negative seek", path, err);
			default:
				throw FileException(path, err);
			}
		}
		#elif defined(JH_OS_LINUX)
		CFileImpl::CFileImpl()
		{
		}

		CFileImpl::CFileImpl(const std::string& path): m_path(path)
		{
			std::string::size_type n = m_path.size();
			if (n > 1 && m_path[n - 1] == '/')
				m_path.resize(n - 1);
		}

		CFileImpl::~CFileImpl()
		{
		}

		void CFileImpl::swapImpl(CFileImpl& file)
		{
			std::swap(m_path, file.m_path);
		}

		void CFileImpl::setPathImpl(const std::string& path)
		{
			m_path = path;
			std::string::size_type n = m_path.size();
			if (n > 1 && m_path[n - 1] == '/')
				m_path.resize(n - 1);
		}

		bool CFileImpl::existsImpl() const
		{
			jh_assert (!m_path.empty());

			struct stat st;
			return stat(m_path.c_str(), &st) == 0;
		}

		bool CFileImpl::canReadImpl() const
		{
			jh_assert (!m_path.empty());

			struct stat st;
			if (stat(m_path.c_str(), &st) == 0)
			{
				if (st.st_uid == geteuid())
					return (st.st_mode & S_IRUSR) != 0;
				else if (st.st_gid == getegid())
					return (st.st_mode & S_IRGRP) != 0;
				else
					return (st.st_mode & S_IROTH) != 0 || geteuid() == 0;
			}
			else handleLastErrorImpl(m_path);
			return false;
		}

		bool CFileImpl::canWriteImpl() const
		{
			jh_assert (!m_path.empty());

			struct stat st;
			if (stat(m_path.c_str(), &st) == 0)
			{
				if (st.st_uid == geteuid())
					return (st.st_mode & S_IWUSR) != 0;
				else if (st.st_gid == getegid())
					return (st.st_mode & S_IWGRP) != 0;
				else
					return (st.st_mode & S_IWOTH) != 0 || geteuid() == 0;
			}
			else handleLastErrorImpl(m_path);
			return false;
		}

		bool CFileImpl::canExecuteImpl() const
		{
			jh_assert (!m_path.empty());

			struct stat st;
			if (stat(m_path.c_str(), &st) == 0)
			{
				if (st.st_uid == geteuid() || geteuid() == 0)
					return (st.st_mode & S_IXUSR) != 0;
				else if (st.st_gid == getegid())
					return (st.st_mode & S_IXGRP) != 0;
				else
					return (st.st_mode & S_IXOTH) != 0;
			}
			else handleLastErrorImpl(m_path);
			return false;
		}

		bool CFileImpl::isFileImpl() const
		{
			jh_assert (!m_path.empty());

			struct stat st;
			if (stat(m_path.c_str(), &st) == 0)
				return S_ISREG(st.st_mode);
			else
				handleLastErrorImpl(m_path);
			return false;
		}

		bool CFileImpl::isDirectoryImpl() const
		{
			jh_assert (!m_path.empty());

			struct stat st;
			if (stat(m_path.c_str(), &st) == 0)
				return S_ISDIR(st.st_mode);
			else
				handleLastErrorImpl(m_path);
			return false;
		}

		bool CFileImpl::isLinkImpl() const
		{
			jh_assert (!m_path.empty());

			struct stat st;
			if (lstat(m_path.c_str(), &st) == 0)
				return S_ISLNK(st.st_mode);
			else
				handleLastErrorImpl(m_path);
			return false;
		}

		bool CFileImpl::isDeviceImpl() const
		{
			jh_assert (!m_path.empty());

			struct stat st;
			if (stat(m_path.c_str(), &st) == 0)
				return S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode);
			else
				handleLastErrorImpl(m_path);
			return false;
		}

		bool CFileImpl::isHiddenImpl() const
		{
			jh_assert (!m_path.empty());
			Path p(m_path);
			p.makeFile();

			return p.getFileName()[0] == '.';
		}

		CTimeStamp CFileImpl::createdImpl() const
		{
			jh_assert (!m_path.empty());

			struct stat st;
			if (stat(m_path.c_str(), &st) == 0)
				return CTimeStamp::fromEpochTime(st.st_ctime);
			else
				handleLastErrorImpl(m_path);
			return 0;
		}

		CTimeStamp CFileImpl::getLastModifiedImpl() const
		{
			jh_assert (!m_path.empty());

			struct stat st;
			if (stat(m_path.c_str(), &st) == 0)
				return CTimeStamp::fromEpochTime(st.st_mtime);
			else
				handleLastErrorImpl(m_path);
			return 0;
		}

		void CFileImpl::setLastModifiedImpl(const CTimeStamp& ts)
		{
			jh_assert (!m_path.empty());

			struct utimbuf tb;
			tb.actime  = ts.epochTime();
			tb.modtime = ts.epochTime();
			if (utime(m_path.c_str(), &tb) != 0)
				handleLastErrorImpl(m_path);
		}

		CFileImpl::FileSizeImpl CFileImpl::getSizeImpl() const
		{
			jh_assert (!m_path.empty());

			struct stat st;
			if (stat(m_path.c_str(), &st) == 0)
				return st.st_size;
			else
				handleLastErrorImpl(m_path);
			return 0;
		}

		void CFileImpl::setSizeImpl(FileSizeImpl size)
		{
			jh_assert (!m_path.empty());

			if (truncate(m_path.c_str(), size) != 0)
				handleLastErrorImpl(m_path);
		}

		void CFileImpl::setWriteableImpl(bool flag)
		{
			jh_assert (!m_path.empty());

			struct stat st;
			if (stat(m_path.c_str(), &st) != 0) 
				handleLastErrorImpl(m_path);
			mode_t mode;
			if (flag)
			{
				mode = st.st_mode | S_IWUSR;
			}
			else
			{
				mode_t wmask = S_IWUSR | S_IWGRP | S_IWOTH;
				mode = st.st_mode & ~wmask;
			}
			if (chmod(m_path.c_str(), mode) != 0) 
				handleLastErrorImpl(m_path);
		}

		void CFileImpl::setExecutableImpl(bool flag)
		{
			jh_assert (!m_path.empty());

			struct stat st;
			if (stat(m_path.c_str(), &st) != 0) 
				handleLastErrorImpl(m_path);
			mode_t mode;
			if (flag)
			{
				mode = st.st_mode | S_IXUSR;
			}
			else
			{
				mode_t wmask = S_IXUSR | S_IXGRP | S_IXOTH;
				mode = st.st_mode & ~wmask;
			}
			if (chmod(m_path.c_str(), mode) != 0) 
				handleLastErrorImpl(m_path);
		}

		void CFileImpl::copyToImpl(const std::string& path) const
		{
			jh_assert (!m_path.empty());

			int sd = open(m_path.c_str(), O_RDONLY);
			if (sd == -1) handleLastErrorImpl(m_path);

			struct stat st;
			if (fstat(sd, &st) != 0) 
			{
				close(sd);
				handleLastErrorImpl(m_path);
			}
			const long blockSize = st.st_blksize;

			int dd = open(path.c_str(), O_CREAT | O_TRUNC | O_WRONLY, st.st_mode & S_IRWXU);
			if (dd == -1)
			{
				close(sd);
				handleLastErrorImpl(path);
			}
			TBuffer<char> buffer(blockSize);
			try
			{
				int n;
				while ((n = read(sd, buffer.begin(), blockSize)) > 0)
				{
					if (write(dd, buffer.begin(), n) != n) 
						handleLastErrorImpl(path);
				}
				if (n < 0)
					handleLastErrorImpl(m_path);
			}
			catch (...)
			{
				close(sd);
				close(dd);
				throw;
			}
			close(sd);
			if (fsync(dd) != 0) 
			{
				close(dd);
				handleLastErrorImpl(path);
			}
			if (close(dd) != 0)
				handleLastErrorImpl(path);
		}

		void CFileImpl::renameToImpl(const std::string& path)
		{
			jh_assert (!m_path.empty());

			if (rename(m_path.c_str(), path.c_str()) != 0)
				handleLastErrorImpl(m_path);
		}

		void CFileImpl::removeImpl()
		{
			jh_assert (!_path.empty());

			int rc;
			if (!isLinkImpl() && isDirectoryImpl())
				rc = rmdir(m_path.c_str());
			else
				rc = unlink(m_path.c_str());
			if (rc) handleLastErrorImpl(m_path);
		}

		bool CFileImpl::createFileImpl()
		{
			jh_assert (!m_path.empty());

			int n = open(m_path.c_str(), O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
			if (n != -1)
			{
				close(n);
				return true;
			}
			if (n == -1 && errno == EEXIST)
				return false;
			else
				handleLastErrorImpl(m_path);
			return false;
		}

		bool CFileImpl::createDirectoryImpl()
		{
			jh_assert (!m_path.empty());

			if (existsImpl() && isDirectoryImpl())
				return false;
			if (mkdir(m_path.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) != 0) 
				handleLastErrorImpl(m_path);
			return true;
		}

		void CFileImpl::handleLastErrorImpl(const std::string& path)
		{
			switch (errno)
			{
			case EIO:
				throw IOException(path, errno);
			case EPERM:
				throw FileAccessDeniedException("insufficient permissions", path, errno);
			case EACCES:
				throw FileAccessDeniedException(path, errno);
			case ENOENT:
				throw FileNotFoundException(path, errno);
			case ENOTDIR:
				throw OpenFileException("not a directory", path, errno);
			case EISDIR:
				throw OpenFileException("not a file", path, errno);
			case EROFS:
				throw FileReadOnlyException(path, errno);
			case EEXIST:
				throw FileExistsException(path, errno);
			case ENOSPC:
				throw FileException("no space left on device", path, errno);
			case EDQUOT:
				throw FileException("disk quota exceeded", path, errno);
		#if !defined(_AIX)
			case ENOTEMPTY:
				throw FileException("directory not empty", path, errno);
		#endif
			case ENAMETOOLONG:
				throw PathSyntaxException(path, errno);
			case ENFILE:
			case EMFILE:
				throw FileException("too many open files", path, errno);
			default:
				throw FileException(std::strerror(errno), path, errno);
			}
		}
		#endif


		/****************
		* CFileImpl   *
		****************/
		CFile::CFile()
		{
		}

		CFile::CFile(const std::string& path) : CFileImpl(path)
		{
		}

		CFile::CFile(const char* path) : CFileImpl(std::string(path))
		{
		}

		CFile::CFile(const CPath& path) : CFileImpl(path.toString())
		{
		}

		CFile::CFile(const CFile& file) : CFileImpl(file.getPathImpl())
		{
		}

		CFile::~CFile()
		{
		}

		CFile& CFile::operator = (const CFile& file)
		{
			setPathImpl(file.getPathImpl());
			return *this;
		}

		CFile& CFile::operator = (const std::string& path)
		{
			setPathImpl(path);
			return *this;
		}

		CFile& CFile::operator = (const char* path)
		{
			jh_check_ptr (path);
			setPathImpl(path);
			return *this;
		}

		CFile& CFile::operator = (const CPath& path)
		{
			setPathImpl(path.toString());
			return *this;
		}

		void CFile::swap(CFile& file)
		{
			swapImpl(file);
		}

		bool CFile::exists() const
		{
			return existsImpl();
		}

		bool CFile::canRead() const
		{
			return canReadImpl();
		}

		bool CFile::canWrite() const
		{
			return canWriteImpl();
		}

		bool CFile::canExecute() const
		{
			return canExecuteImpl();
		}

		bool CFile::isFile() const
		{
			return isFileImpl();
		}

		bool CFile::isDirectory() const
		{
			return isDirectoryImpl();
		}

		bool CFile::isLink() const
		{
			return isLinkImpl();
		}

		bool CFile::isDevice() const
		{
			return isDeviceImpl();
		}

		bool CFile::isHidden() const
		{
			return isHiddenImpl();
		}

		CTimeStamp CFile::created() const
		{
			return createdImpl();
		}

		CTimeStamp CFile::getLastModified() const
		{
			return getLastModifiedImpl();
		}

		CFile& CFile::setLastModified(const CTimeStamp& ts)
		{
			setLastModifiedImpl(ts);
			return *this;
		}

		CFile::FileSize CFile::getSize() const
		{
			return getSizeImpl();
		}

		CFile& CFile::setSize(FileSize size)
		{
			setSizeImpl(size);
			return *this;
		}

		CFile& CFile::setWriteable(bool flag)
		{
			setWriteableImpl(flag);
			return *this;
		}

		CFile& CFile::setReadOnly(bool flag)
		{
			setWriteableImpl(!flag);
			return *this;
		}

		CFile& CFile::setExecutable(bool flag)
		{
			setExecutableImpl(flag);
			return *this;
		}

		void CFile::copyTo(const std::string& path) const
		{
			CPath src(getPathImpl());
			CPath dest(path);
			CFile destFile(path);
			if ((destFile.exists() && destFile.isDirectory()) || dest.isDirectory())
			{
				dest.makeDirectory();
				dest.setFileName(src.getFileName());
			}
			if (isDirectory())
				copyDirectory(dest.toString());
			else
				copyToImpl(dest.toString());
		}

		void CFile::copyDirectory(const std::string& path) const
		{
			CFile target(path);
			target.createDirectories();

			CPath src(getPathImpl());
			src.makeFile();
			CDirectoryIterator it(src);
			CDirectoryIterator end;
			for (; it != end; ++it)
			{
				it->copyTo(path);
			}
		}

		void CFile::moveTo(const std::string& path)
		{
			copyTo(path);
			remove(true);
			setPathImpl(path);
		}

		void CFile::renameTo(const std::string& path)
		{
			renameToImpl(path);
			setPathImpl(path);
		}

		void CFile::remove(bool recursive)
		{
			if (recursive && !isLink() && isDirectory())
			{
				std::vector<CFile> files;
				list(files);
				for (std::vector<CFile>::iterator it = files.begin(); it != files.end(); ++it)
				{
					it->remove(true);
				}
			}
			removeImpl();
		}

		bool CFile::createFile()
		{
			return createFileImpl();
		}

		bool CFile::createDirectory()
		{
			return createDirectoryImpl();
		}

		void CFile::createDirectories()
		{
			if (!exists())
			{
				CPath p(getPathImpl());
				p.makeDirectory();
				if (p.depth() > 1)
				{
					p.makeParent();
					CFile f(p);
					f.createDirectories();
				}
				createDirectoryImpl();
			}
		}

		void CFile::list(std::vector<std::string>& files) const
		{
			files.clear();
			CDirectoryIterator it(*this);
			CDirectoryIterator end;
			while (it != end)
			{
				files.push_back(it.name());
				++it;
			}
		}

		void CFile::list(std::vector<CFile>& files) const
		{
			files.clear();
			CDirectoryIterator it(*this);
			CDirectoryIterator end;
			while (it != end)
			{
				files.push_back(*it);
				++it;
			}
		}

		void CFile::handleLastError(const std::string& path)
		{
			handleLastErrorImpl(path);
		}

	}//end of namespace filesys
}//end of namespace JHCPP